/*
 * The contents of this web application are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this web application except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the License.
 * 
 * The Original Code is owned by and the Initial Developer of the Original Code is 
 * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
 * 
 * Section 11 of the License is EXPRESSLY amended to include a provision stating 
 * that any dispute, including but not limited to disputes related to the enforcement 
 * of the License, to which Composite A/S as owner of the Original Code, as Initial 
 * Developer or in any other role, becomes a part to shall be governed by Danish law 
 * and be initiated before the Copenhagen City Court ("K�benhavns Byret")            
 */

using System;
using System.Collections.Generic;
using System.Linq;
using Composite.Core.Extensions;


namespace Composite.Core.Collections
{
    internal sealed class NamespaceTreeBuilder
    {
        private NamespaceTreeBuilderFolder _rootFolder;
        private char _namespaceSeparator;


        public NamespaceTreeBuilder(IEnumerable<INamespaceTreeBuilderLeafInfo> namespaceTreeBuilderInfos)
            : this(namespaceTreeBuilderInfos, '.')
        {
        }



        public NamespaceTreeBuilder(IEnumerable<INamespaceTreeBuilderLeafInfo> namespaceTreeBuilderInfos, char namespaceSeparator)
        {
            if (namespaceTreeBuilderInfos == null) throw new ArgumentNullException("namespaceTreeBuilderInfos");
            _namespaceSeparator = namespaceSeparator;

            _rootFolder = new NamespaceTreeBuilderFolder("", "");

            foreach (INamespaceTreeBuilderLeafInfo namespaceTreeBuilderInfo in namespaceTreeBuilderInfos)
            {
                NamespaceTreeBuilderFolder folderNode = GetOrCreateFolder(namespaceTreeBuilderInfo.Namespace);

                folderNode.Leafs.Add(namespaceTreeBuilderInfo);
            }
        }



        public NamespaceTreeBuilderFolder RootFolder
        {
            get { return _rootFolder; }
        }



        public NamespaceTreeBuilderFolder GetFolder(string namespaceName)
        {
            if (namespaceName == null) throw new ArgumentNullException("namespaceName");
            if (namespaceName.IsCorrectNamespace(_namespaceSeparator) == false) throw new ArgumentException(string.Format("The namespace '{0}' is not correctly formattet", namespaceName));

            NamespaceTreeBuilderFolder currentNode = _rootFolder;
            string[] namespaceComponents = namespaceName.Split(_namespaceSeparator);
            foreach (string namespaceComponent in namespaceComponents.Where(s => s != ""))
            {
                currentNode = currentNode.SubFolders.Where(n => n.Name == namespaceComponent).FirstOrDefault();

                if (currentNode == null)
                {
                    return null;
                }
            }

            return currentNode;
        }



        public NamespaceTreeBuilderFolder FindFolder(Func<NamespaceTreeBuilderFolder, bool> predicate)
        {
            return FindFolder(_rootFolder, predicate);
        }



        private NamespaceTreeBuilderFolder FindFolder(NamespaceTreeBuilderFolder currentFolder, Func<NamespaceTreeBuilderFolder, bool> predicate)
        {
            if (predicate(currentFolder)) return currentFolder;

            foreach (NamespaceTreeBuilderFolder subFolder in currentFolder.SubFolders)
            {
                NamespaceTreeBuilderFolder foundFolder = FindFolder(subFolder, predicate);

                if (foundFolder != null)
                {
                    return foundFolder;
                }
            }

            return null;
        }



        private NamespaceTreeBuilderFolder GetOrCreateFolder(string namespaceName)
        {
            if (namespaceName.IsCorrectNamespace(_namespaceSeparator) == false) throw new ArgumentException(string.Format("The namespace '{0}' is not correctly formattet", namespaceName != null ? namespaceName : "(null)"));

            string[] namespaceComponents = namespaceName.Split(_namespaceSeparator);

            if ((namespaceComponents.Length == 1) && (namespaceComponents[0] == ""))
            {
                return _rootFolder;
            }

            NamespaceTreeBuilderFolder currentNode = _rootFolder;
            string currentNamespace = "";

            foreach (string namespaceComponent in namespaceComponents)
            {
                NamespaceTreeBuilderFolder node = currentNode.SubFolders.Where(n => n.Name == namespaceComponent).FirstOrDefault();

                if (node == null)
                {
                    node = new NamespaceTreeBuilderFolder(namespaceComponent, currentNamespace);
                    currentNode.SubFolders.Add(node);
                }

                if (currentNamespace == "")
                {
                    currentNamespace = namespaceComponent;
                }
                else
                {
                    currentNamespace = string.Format("{0}{1}{2}", currentNamespace, _namespaceSeparator, namespaceComponent);
                }

                currentNode = node;
            }

            return currentNode;
        }
    }
}
